package com.xnxkj.wx.micro.mch.utils;

import com.xnxkj.wx.micro.mch.bean.message.request.RequestMessage;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class SignUtils {
    /**
     * 全局签名忽略字段
     */
    private static final Set<String> GLOBAL_IGNORE_PROPERTIES = Stream.of("sign", "key", "xmlString", "xmlDoc", "couponList").collect(Collectors.toSet());

    /**
     * 签名类型
     */
    public interface SignType{
        String HMAC_SHA256 = "HMAC-SHA256";
        String MD5 = "MD5";
    }

    /**
     * 生成签名并存入map中，此方法会自动填充signType和nonceStr
     * @param params 消息参数
     * @param key 商户key
     * @param signType 签名类型
     * @param ignoreProperties 忽略字段
     */
    public static void generateSign(SortedMap<String, String> params, String key, String signType, String... ignoreProperties){
        params.put("nonce_str", StringUtils.randomString(32));
        params.put("sign_type", signType);
        String sign = getSign(params, key, signType, ignoreProperties);
        params.put("sign", sign);
    }

    /**
     * 生成签名并设置到消息中，signType会自动填充
     * @param message 消息内容
     * @param key 商户key
     * @param signType 签名类型
     */
    public static void generateSign(RequestMessage message, String key, String signType){
        message.setNonceStr(StringUtils.randomString(32));
        message.setSignType(signType);
        SortedMap<String, String> messageMap = message.toMap();
        message.setSign(getSign(messageMap, key, signType));
    }

    /**
     * 获取签名
     * @param params 参数列表
     * @param key 秘钥
     * @param signType 签名类型
     * @param ignoreProperties 忽略字段
     */
    public static String getSign(SortedMap<String, String> params, String key, String signType, String... ignoreProperties){
        Iterator<String> keyIterator = params.keySet().iterator();
        Set<String> ignorePropertySet = Stream.of(ignoreProperties).collect(Collectors.toSet());
        StringBuilder signStringBuilder = new StringBuilder();
        while (keyIterator.hasNext()){
            String k = keyIterator.next();
            String v = params.get(k);
            if(StringUtils.isEmpty(v) || ignorePropertySet.contains(k) || GLOBAL_IGNORE_PROPERTIES.contains(k)){
                continue;
            }
            signStringBuilder.append(k).append("=").append(v).append("&");
        }
        signStringBuilder.append("key=").append(key);
        if (signType.equals(SignType.HMAC_SHA256)) {
            return createHmacSha256Sign(signStringBuilder.toString(), key);
        }
        return DigestUtils.md5Hex(signStringBuilder.toString()).toUpperCase();
    }

    /**
     * HMAC_SHA256签名
     * @param message 签名内容
     * @param key 秘钥
     */
    private static String createHmacSha256Sign(String message, String key) {
        try {
            Mac sha256 = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            sha256.init(secretKeySpec);
            byte[] bytes = sha256.doFinal(message.getBytes(StandardCharsets.UTF_8));
            return Hex.encodeHexString(bytes).toUpperCase();
        } catch (InvalidKeyException | NoSuchAlgorithmException var5) {
            throw new RuntimeException(var5);
        }
    }
}
